/**
 * Http请求包装对象
 * 参考： iview https://gitee.com/icarusion/iview-admin/blob/master/src/libs/axios.js
 *
 * @author will
 * @site https://proxy.jdyd.xyz
 * @date 2021/5/8 07:18
 */

import axios from 'axios'
import storage from '@/utils/gzStorageWrapper'
import appConfig from '@/config/appConfig'
import $infoBox from '@/utils/infoBox'
import { useUserStore } from '@/store/modules/user'
import { useOem } from '@/store/modules/oem'
import {
  sm4DecryptByResData,
  sm4EncryptByReqData,
  updateStoreVersionAndKey,
} from '@/utils/encryptUtil'
import { useRouter } from '@/utils/routerUtils.js'
import store from 'store'

// 不加密借口列表
const noEncryptApiList = [
  '/api/fans/getWxchatPayOpenid',
  '/api/fans/getPlaceWxchatOpenid',
  '/api/anon/auth/login',
  '/api/sms/smsCode',
  '/api/officialMedia/uploadMedia',
  '/api/anon/siteInfos'
]

class HttpRequest {
  constructor(baseUrl = process.env.VITE_API_BASE_URL) {
    this.baseUrl = baseUrl
    this.queue = {} // 发送队列, 格式为: {请求url: true}, 可以做一些验证之类
    this.requestQueue = [] // 请求队列，用于串行执行请求
    this.isRequesting = false // 标记当前是否有请求正在进行
  }

  // 基础配置信息
  baseConfig() {
    const headers = {}
    headers[appConfig.ACCESS_TOKEN_NAME] = storage.getToken()
    headers[appConfig.ACCESS_VERSION] = store.get('dynamicKey')?.version
    headers['platform'] = 'pc'
    return {
      baseURL: this.baseUrl,
      headers: headers,
    }
  }

  destroy(url, showLoading) {
    delete this.queue[url]
  }

  interceptors(instance, url, showErrorMsg, showLoading) {
    // 请求拦截
    instance.interceptors.request.use(
      (config) => {
        // 添加全局的loading...
        if (!Object.keys(this.queue).length && showLoading) {
          this.myGlobalLoadingFunc(true) // 加载中显示loading组件
        }
        this.queue[url] = true

        if (
          store.get('dynamicKey')?.dataKey != undefined &&
          store.get('dynamicKey')?.version != undefined
        ) {
          // 加密数据
          if (
            config.data &&
            useOem().getHttpMessageEncryptFlag() &&
            !noEncryptApiList.includes(config.url)
          ) {
            config.data = sm4EncryptByReqData(config.data)
          }
        }

        return config
      },
      (error) => {
        this.myGlobalLoadingFunc(false) // 报错关闭loading组件
        return Promise.reject(error)
      }
    )

    // 响应拦截
    instance.interceptors.response.use(
      (res) => {
        this.destroy(url, showLoading)

        if (showLoading) {
          this.myGlobalLoadingFunc(false) // 报错关闭loading组件
        }
        const resData = res.data // 接口实际返回数据 格式为：{code: '', msg: '', data: ''}， res.data 是axios封装对象的返回数据；
        if (resData.code !== 0) {
          // 相应结果不为0， 说明异常
          if (showErrorMsg) {
            $infoBox.message.error(resData.msg) // 显示异常信息
          }
          if (resData.code == 401) {
            // 无访问权限，会话超时， 提示用户信息 & 退出系统
            setTimeout(() => {
              useUserStore().logout()
              // 清除本地缓存
              localStorage.clear()        
            }, 2000)
          }
          if (resData.code == 5005) {
            // 密码已过期， 直接跳转到更改密码页面
            useRouter().push({
              path: '/current/userinfo',
              query: { isPwdExpired: 1 },
            })
          }
          return Promise.reject(resData)
        } else {
          // 加密数据
          if (!resData.data && resData.encryptData) {
            const { data: decryptData, dynamicKey } = sm4DecryptByResData(
              resData.encryptData
            )
            store.set('dynamicKey', dynamicKey)
            return decryptData
          } else {
            if (
              resData.data?.dynamicKey?.version !== void 0 &&
              resData.data?.dynamicKey?.dataKey
            ) {
              store.set('dynamicKey', resData.data.dynamicKey)
            }
            return resData.data
          }
        }
      },
      (error) => {
        this.destroy(url, showLoading)
        if (showLoading) {
          this.myGlobalLoadingFunc(false) // 报错关闭loading组件
        }

        let errorInfo =
          error.response && error.response.data && error.response.data.data
        if (!errorInfo) {
          errorInfo = error.response.data
        }

        if (error.response.status === 401) {
          // 无访问权限，会话超时， 提示用户信息 & 退出系统
          useUserStore().logout()
          // 清除本地缓存
          localStorage.clear()
          $infoBox.confirmDanger(
            '会话超时，请重新登录',
            '3s后将自动退出...',
            () => {},
            { okText: '重新登录', cancelText: '关闭对话' }
          )
        } else {
          if (showErrorMsg) {
           
            $infoBox.message.error(JSON.stringify(errorInfo)) // 显示异常信息
          }
        }

        return Promise.reject(errorInfo)
      }
    )
  }

  // 串行执行请求
  async executeRequest(options) {
    // 如果当前没有请求在执行，直接执行
    if (!this.isRequesting) {
      this.isRequesting = true
      try {
        const instance = axios.create()
        const config = Object.assign(this.baseConfig(), options)
        this.interceptors(
          instance,
          config.url,
          options.showErrorMsg,
          options.showLoading
        )
        const response = await instance(config)
        return response
      } finally {
        this.isRequesting = false
        this.processNextRequest() // 处理下一个请求
      }
    } else {
      // 如果有请求在执行，将当前请求加入队列
      return new Promise((resolve, reject) => {
        this.requestQueue.push({ options, resolve, reject })
      })
    }
  }

  // 处理下一个请求
  processNextRequest() {
    if (this.requestQueue.length > 0) {
      const nextRequest = this.requestQueue.shift() // 取出队列中的下一个请求
      this.executeRequest(nextRequest.options)
        .then(nextRequest.resolve)
        .catch(nextRequest.reject)
    }
  }

  // interceptorsFlag： 是否进行自定义拦截器处理，默认为： true
  // showErrorMsg 发送请求出现异常是否全局提示错误信息
  // showLoading 发送请求前后显示全局loading
  request(
    options,
    interceptorsFlag = true,
    showErrorMsg = true,
    showLoading = false
  ) {
    // 将请求加入队列并串行执行
    return this.executeRequest({ ...options, showErrorMsg, showLoading })
  }

  // 下载二进制文件
  exportExcel(url, params, method = 'GET', successFunc) {
    this.myGlobalLoadingFunc(true) // loading
    return new Promise((resolve, reject) => {
      // 加密数据
      if (params && useOem().getHttpMessageEncryptFlag()) {
        params = sm4EncryptByReqData(params)
      }
      axios
        .request({
          url: this.baseConfig().baseURL + url,
          headers: this.baseConfig().headers,
          method,
          params: method === 'GET' ? params : null,
          data: method === 'POST' ? params : null,
          responseType: 'blob',
          onDownloadProgress: (evt) => {
            // this.myGlobalLoadingFunc(true)
          },
        })
        .then((res) => {
          this.myGlobalLoadingFunc(false) // 报错关闭loading组件
          // 说明系统异常
          if (res.data.type == 'application/json') {
            res.data.text().then((params) => {
              let obj = JSON.parse(params)
              $infoBox.message.error(obj.msg)
            })
            reject('')
          } else {
            // resolve到具体页面使用js-file-download【https://www.npmjs.com/package/js-file-download】下载
            /* const blob = new Blob([res.data]);
          const url = URL.createObjectURL(blob);
          const link = document.createElement('a');
          link.href = url;
          link.download = title;
          link.click(); */
            resolve(res)
          }
        })
        .catch((err) => {
          this.myGlobalLoadingFunc(false) // 报错关闭loading组件
          
          reject(err)
        })
    })
  }
 // 下载二进制文件
  exportZip(url, params, method = 'GET', successFunc) {
    this.myGlobalLoadingFunc(true) // loading
    return new Promise((resolve, reject) => {
      // 加密数据
      // if (params && useOem().getHttpMessageEncryptFlag()) {
      //   params = sm4EncryptByReqData(params);
      // }
      axios
        .request({
          url: this.baseConfig().baseURL + url,
          headers: this.baseConfig().headers,
          method,
          params: method === 'GET' ? params : null,
          data: method === 'POST' ? params : null,
          responseType: 'blob',
          onDownloadProgress: (evt) => {
            // this.myGlobalLoadingFunc(true)
          },
        })
        .then((res) => {
          this.myGlobalLoadingFunc(false) // 报错关闭loading组件
          // 说明系统异常
          if (res.data.type == 'application/json') {
            res.data.text().then((params) => {
              let obj = JSON.parse(params)
              $infoBox.message.error(obj.msg)
            })
            reject('')
          } else {
            // resolve到具体页面使用js-file-download【https://www.npmjs.com/package/js-file-download】下载
            /* const blob = new Blob([res.data]);
          const url = URL.createObjectURL(blob);
          const link = document.createElement('a');
          link.href = url;
          link.download = title;
          link.click(); */
            resolve(res)
          }
        })
        .catch((err) => {
          this.myGlobalLoadingFunc(false) // 报错关闭loading组件
          
          reject(err)
        })
    })
  }
  myGlobalLoadingFunc(isShowGlobalLoadingVal) {
    useUserStore().setGlobalLoading(isShowGlobalLoadingVal)
  }
}
export default HttpRequest
